// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.

package nachos.machine;

import nachos.security.*;

/**
 * A hardware timer generates a CPU timer interrupt approximately every 500
 * clock ticks. This means that it can be used for implementing time-slicing, or
 * for having a thread go to sleep for a specific period of time.
 * 
 * The <tt>Timer</tt> class emulates a hardware timer by scheduling a timer
 * interrupt to occur every time approximately 500 clock ticks pass. There is a
 * small degree of randomness here, so interrupts do not occur exactly every 500
 * ticks.
 */
public final class Timer {
	/**
	 * Allocate a new timer.
	 * 
	 * @param privilege
	 *            encapsulates privileged access to the Nachos machine.
	 */
	public Timer(Privilege privilege) {
		System.out.print(" timer");

		this.privilege = privilege;

		timerInterrupt = new Runnable() {
			public void run() {
				timerInterrupt();
			}
		};

		autoGraderInterrupt = new Runnable() {
			public void run() {
				Machine.autoGrader().timerInterrupt(Timer.this.privilege,
						lastTimerInterrupt);
			}
		};

		scheduleInterrupt();
	}

	/**
	 * Set the callback to use as a timer interrupt handler. The timer interrupt
	 * handler will be called approximately every 500 clock ticks.
	 * 
	 * @param handler
	 *            the timer interrupt handler.
	 */
	public void setInterruptHandler(Runnable handler) {
		this.handler = handler;
	}

	/**
	 * Get the current time.
	 * 
	 * @return the number of clock ticks since Nachos started.
	 */
	public long getTime() {
		return privilege.stats.totalTicks;
	}

	private void timerInterrupt() {
		scheduleInterrupt();
		scheduleAutoGraderInterrupt();

		lastTimerInterrupt = getTime();

		if (handler != null)
			handler.run();
	}

	private void scheduleInterrupt() {
		int delay = Stats.TimerTicks;
		delay += Lib.random(delay / 10) - (delay / 20);

		privilege.interrupt.schedule(delay, "timer", timerInterrupt);
	}

	private void scheduleAutoGraderInterrupt() {
		privilege.interrupt.schedule(1, "timerAG", autoGraderInterrupt);
	}

	private long lastTimerInterrupt;
	private Runnable timerInterrupt;
	private Runnable autoGraderInterrupt;

	private Privilege privilege;
	private Runnable handler = null;
}
